package com.dant.managers
{
	import com.dant.components.RubberBand;
	import com.dant.interfaces.IResizeManager;
	
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import mx.core.Application;
	import mx.core.IFlexDisplayObject;
	import mx.core.UIComponent;
	import mx.utils.UIDUtil;

	public class ComponentResizeManager extends EventDispatcher implements IResizeManager
	{
		// DO NOT CHANGE! Used to build the resize types allowed array (registerObjectToResizeMap)
        protected const CONST_RESIZE_TOP_LEFT:Number = 0;
        protected const CONST_RESIZE_TOP_CENTER:Number = 1;
        protected const CONST_RESIZE_TOP_RIGHT:Number = 2;
        protected const CONST_RESIZE_RIGHT:Number = 3;
        protected const CONST_RESIZE_BOTTOM_RIGHT:Number = 4;
        protected const CONST_RESIZE_BOTTOM_CENTER:Number = 5;
        protected const CONST_RESIZE_BOTTOM_LEFT:Number = 6;
        protected const CONST_RESIZE_LEFT:Number = 7;

        // resize properties
        private var _resizeButtonRadius:Number = 10;
        private var _resizableComponents:Boolean = true;
        private var currentWidth:Number;
        private var currentHeight:Number;
        private var currentResizeMethod:Number;
        private var rubberBand:RubberBand;
        private var resizingObject:UIComponent;
        private var startResizePosition:Point;
        private var registeredResizableObjectsMap:Object = {};

        [Bindable]
        public function set resizeButtonRadius ( value:Number ):void
        {
            if ( value > 0 && ( resizeButtonRadius != value ) )
            {
                _resizeButtonRadius = value;
            }
        }

        public function get resizeButtonRadius ( ):Number
        {
            return ( _resizeButtonRadius );
        }

        [Bindable]
        public function set resizableComponents ( value:Boolean ):void
        {
            _resizableComponents = value;
        }

        public function get resizableComponents ( ):Boolean
        {
            return ( _resizableComponents );
        }


		public function ComponentResizeManager(target:IEventDispatcher=null)
		{
			super(target);
		}

        private function registerObjectToResizeMap ( object:UIComponent,
                				topLeftResize:Boolean, topResize:Boolean, topRightResize:Boolean, 
        				rightResize:Boolean, bottomRightResize:Boolean, bottomResize:Boolean, 
        				bottomLeftResize:Boolean, leftResize:Boolean ):void
        {
        	// an array builded with the resize types allowed in the index
        	// of their constants
        	var allowedResizes:Array = [
        							topLeftResize,
        							topResize,
        							topRightResize,
        							rightResize,
        							bottomRightResize,
        							bottomResize,
        							bottomLeftResize,
        							leftResize
        							];
        	
            registeredResizableObjectsMap [ UIDUtil.getUID(object) ] = allowedResizes;
        }

        public function registerObjectToResize ( object:UIComponent, 
        				topLeftResize:Boolean=true, topResize:Boolean=true, 
        				topRightResize:Boolean=true, rightResize:Boolean=true, 
        				bottomRightResize:Boolean=true, bottomResize:Boolean=true, 
        				bottomLeftResize:Boolean=true, leftResize:Boolean=true ):UIComponent
        {
        	if ( ! objectIsResizable( object ) )
        	{
                object.addEventListener(MouseEvent.MOUSE_DOWN, object_mouseDownHandler,false, 10, true);
	            registerObjectToResizeMap( object, topLeftResize, topResize, topRightResize,
	            						   rightResize, bottomRightResize, bottomResize, 
	            						   bottomLeftResize, leftResize );
        	}
            return ( object );
        }

        public function objectIsResizable ( object:UIComponent ):Boolean
        {
            return ( registeredResizableObjectsMap[ UIDUtil.getUID(object) ] != null );
        }

        public function isResizingObject ( object:UIComponent ):Boolean
        {
            return ( resizingObject == object );
        }

        private function unregisterObjectFromResizeMap ( object:UIComponent ):void
        {
             delete (registeredResizableObjectsMap [ UIDUtil.getUID(object) ]);
        }

        public function unregisterObjectToResize ( object:UIComponent ):UIComponent
        {
        	if ( objectIsResizable( object ) )
        	{
                object.removeEventListener(MouseEvent.MOUSE_DOWN, object_mouseDownHandler);
	            unregisterObjectFromResizeMap( object );
            }
            return ( object );
        }

		protected function getAllowedResizesForObject (object:UIComponent):Array
		{
			return (registeredResizableObjectsMap[UIDUtil.getUID(object)]);
		}

        //--------------------------------------
		//  Focus control
		//--------------------------------------
        protected function calculateTopLeftResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( x+resizeButtonRadius, y+resizeButtonRadius );
            return ( result );
        }

        protected function calculateTopRightResizeButton ( x:Number, y:Number, width:Number, height:Number  ):Point
        {
            var result:Point = new Point ( x + width - resizeButtonRadius, y + resizeButtonRadius );
            return ( result );
        }

        protected function calculateBottomLeftResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( x + resizeButtonRadius, y + height - resizeButtonRadius );
            return ( result );
        }

        protected function calculateBottomRightResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( x + width - resizeButtonRadius, y + height - resizeButtonRadius);
            return ( result );
        }

        protected function calculateTopCenterResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( (x+width)/2 , y + resizeButtonRadius );
            return ( result );
        }

        protected function calculateBottomCenterResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( (x+width)/2 , (y+height) - resizeButtonRadius );
            return ( result );
        }

        protected function calculateRightCenterResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( x+width-resizeButtonRadius , (y+height)/2 );
            return ( result );
        }

        protected function calculateLeftCenterResizeButton ( x:Number, y:Number, width:Number, height:Number ):Point
        {
            var result:Point = new Point ( x+resizeButtonRadius, (y+height)/2 );
            return ( result );
        }

		protected function calculateDimensions( rubberBand:UIComponent, associatedObject:UIComponent, 
																xPlus:Number, yPlus:Number ):Rectangle
		{
            var newWidth:Number = 0;
            var newHeight:Number = 0;
            var newX:Number = 0;
            var newY:Number = 0;
            switch ( currentResizeMethod )
            {
                case CONST_RESIZE_BOTTOM_RIGHT:
                {
                    newWidth = setNewWidth ( associatedObject, xPlus );
                    newHeight = setNewHeight( associatedObject, yPlus );
                    newX = rubberBand.x;
                    newY = rubberBand.y;
                    break;
                }
                case CONST_RESIZE_BOTTOM_CENTER:
                {
                    newWidth = rubberBand.width;
                    newHeight = setNewHeight( associatedObject, yPlus );
                    newX = rubberBand.x;
                    newY = rubberBand.y;
                    break;
                }
                case CONST_RESIZE_BOTTOM_LEFT:
                {
                    newWidth = setNewWidth ( associatedObject, (xPlus*-1) );
                    newHeight = setNewHeight ( associatedObject, yPlus );
                    newX =  setNewPositionX ( associatedObject, (xPlus*-1) );
                    newY = rubberBand.y;
                    break;
                }
                case CONST_RESIZE_TOP_RIGHT:
                {
                    newWidth = setNewWidth ( associatedObject, (xPlus) );
                    newHeight = setNewHeight ( associatedObject, (yPlus*-1) );
                    newX = rubberBand.x;
                    newY = setNewPositionY ( associatedObject, (yPlus*-1));
                    break;
                }
                case CONST_RESIZE_TOP_LEFT:
                {
                    newWidth = setNewWidth ( associatedObject, (xPlus*-1) );
                    newHeight = setNewHeight ( associatedObject, (yPlus*-1) );
                    newX = setNewPositionX ( associatedObject, (xPlus*-1) );
                    newY = setNewPositionY( associatedObject, (yPlus*-1) );
                    break;
                }
                case CONST_RESIZE_TOP_CENTER:
                {
                    newWidth = rubberBand.width;
                    newHeight = setNewHeight ( associatedObject, (yPlus*-1) );
                    newX = rubberBand.x;
                    newY = setNewPositionY ( associatedObject, (yPlus*-1) );
                    break;
                }
                case CONST_RESIZE_LEFT:
                {
                    newWidth = setNewWidth ( associatedObject, (xPlus*-1) );
                    newHeight = rubberBand.height;
                    newX = setNewPositionX ( associatedObject, (xPlus*-1) );
                    newY = rubberBand.y;
                    break;
                }
                case CONST_RESIZE_RIGHT:
                {
                    newWidth = setNewWidth ( associatedObject, xPlus );
                    newHeight = rubberBand.height;
                    newX = rubberBand.x;
                    newY = rubberBand.y;
                    break;
                }
            }
            
            return (new Rectangle(newX, newY, newWidth, newHeight));
		}

		//--------------------------------------
		//  Resize
		//--------------------------------------
        protected function validateResizePoint ( clickedX:Number, clickedY:Number, resizeButtonPoint:Point ):Boolean
        {
            return ( (clickedX < (resizeButtonPoint.x+resizeButtonRadius) && clickedX > (resizeButtonPoint.x - resizeButtonRadius) ) &&
            ( clickedY < (resizeButtonPoint.y + resizeButtonRadius) && clickedY > (resizeButtonPoint.y - resizeButtonRadius) ) );
        }

        public function bottomCenterButtonPosition ( object:UIComponent ):Point 
        {
            return (calculateBottomCenterResizeButton( 0, 0, object.width, object.height ));
        }

        public function bottomLeftButtonPosition ( object:UIComponent ):Point 
        {
            return (calculateBottomLeftResizeButton( 0, 0, object.width, object.height ));
        }

        public function bottomRightButtonPosition ( object:UIComponent ):Point
        {
            return (calculateBottomRightResizeButton( 0, 0, object.width, object.height )); 
        }

        public function leftCenterButtonPosition ( object:UIComponent ):Point
        {
            return (calculateLeftCenterResizeButton( 0, 0, object.width, object.height ));
        }

        public function rightCenterButtonPosition ( object:UIComponent ):Point
        {
            return (calculateRightCenterResizeButton( 0, 0, object.width, object.height ));
        }

        public function topCenterButtonPosition ( object:UIComponent ):Point
        {
            return (calculateTopCenterResizeButton( 0, 0, object.width, object.height ));
        }

        public function topLeftButtonPosition ( object:UIComponent ):Point 
        {
            return (calculateTopLeftResizeButton( 0, 0, object.width, object.height ));
        }

        public function topRightButtonPosition ( object:UIComponent ):Point
        {
            return (calculateTopRightResizeButton( 0, 0, object.width, object.height ));
        }

        protected function findResizeType ( objectToCheck:UIComponent, x:Number,y:Number ):Number
        {
            var bottomCenterButtonPosition:Point = bottomCenterButtonPosition ( objectToCheck );
            var bottomLeftButtonPosition:Point = bottomLeftButtonPosition ( objectToCheck );
            var bottomRightButtonPosition:Point = bottomRightButtonPosition ( objectToCheck ); 
            var leftCenterButtonPosition:Point = leftCenterButtonPosition ( objectToCheck );
            var rightCenterButtonPosition:Point = rightCenterButtonPosition ( objectToCheck );
            var topCenterButtonPosition:Point = topCenterButtonPosition ( objectToCheck );
            var topLeftButtonPosition:Point = topLeftButtonPosition ( objectToCheck );
            var topRightButtonPosition:Point = topRightButtonPosition ( objectToCheck );

            if ( validateResizePoint( x, y, bottomRightButtonPosition ) ) 
            {
                return ( CONST_RESIZE_BOTTOM_RIGHT );
            }
            else if ( validateResizePoint( x, y, bottomCenterButtonPosition ) )
            {
                return  ( CONST_RESIZE_BOTTOM_CENTER );
            }
            else if ( validateResizePoint( x, y, bottomLeftButtonPosition ) )
            {
                return  ( CONST_RESIZE_BOTTOM_LEFT );
            }
            else if ( validateResizePoint( x, y, topCenterButtonPosition ) )
            {
                return  ( CONST_RESIZE_TOP_CENTER );
            }
            else if ( validateResizePoint( x, y, topLeftButtonPosition ) )
            {
                return  ( CONST_RESIZE_TOP_LEFT );
            }
            else if ( validateResizePoint( x, y, topRightButtonPosition ) )
            {
                return  ( CONST_RESIZE_TOP_RIGHT );
            }
            else if ( validateResizePoint( x, y, rightCenterButtonPosition ) )
            {
                return  ( CONST_RESIZE_RIGHT );
            }
            else if ( validateResizePoint( x, y, leftCenterButtonPosition ) )
            {
                return  ( CONST_RESIZE_LEFT );
            }
            return ( -1 );
        }

        protected function setComponentNewSize ( componentToResize:IFlexDisplayObject, setterComponent:UIComponent ):void
        {
//          componentToResize.move( setterComponent.x, setterComponent.y );
            componentToResize.x = setterComponent.x;
            componentToResize.y = setterComponent.y;
//          componentToResize.setActualSize( setterComponent.width, setterComponent.height );
            componentToResize.width = setterComponent.width;
            componentToResize.height = setterComponent.height;
            componentToResize.visible = true; 
        }

        protected function setNewHeight ( associatedObject:UIComponent, yPlus:Number ):Number
        {
            return ( ( associatedObject.height + yPlus > associatedObject.minHeight ) ?
            ( associatedObject.height + yPlus ):associatedObject.minHeight );
/*          if 
            {
                return ;
            }
 */     }

        protected function setNewWidth ( associatedObject:UIComponent, xPlus:Number ):Number
        {
            return ( ( associatedObject.width + xPlus > associatedObject.minWidth ) ?
            ( associatedObject.width + xPlus ):associatedObject.minWidth );
/*          if 
            { 
                return ( associatedObject.width + xPlus );
            }
 */     }

        protected function setNewPositionX ( associatedObject:UIComponent, xPlus:Number ):Number
        {
            return ( (associatedObject.x - xPlus) > 0 ?
                 (associatedObject.x - xPlus):0 );
//          return ( ( xPlus > 0 ) ? xPlus:0 ); 
/*          if ( xPlus > 0 ) 
            {
                return ( xPlus );
            }
 */     }

        protected function setNewPositionY ( associatedObject:UIComponent, yPlus:Number ):Number
        {
            return ( (associatedObject.y - yPlus) > 0 ?
                 (associatedObject.y - yPlus):0 );
//          return ( ( yPlus > 0 ) ? yPlus:0 );
/*          if  
            {
                return ( yPlus );
            }
 */     }
        
        protected function setNewSize ( rubberBand:UIComponent, associatedObject:UIComponent, xPlus:Number, yPlus:Number ):void
        {
            var dimension:Rectangle = calculateDimensions(rubberBand,associatedObject,xPlus,yPlus);
            
//          rubberBand.move ( newX, newY );
//            rubberBand.setActualSize( newWidth, newHeight );
            rubberBand.x = dimension.x;
            rubberBand.y = dimension.y;
            rubberBand.width = dimension.width;
            rubberBand.height = dimension.height;
        }

//--------------------------------------------------------------------------
//
//  Event handlers
//
//--------------------------------------------------------------------------

		protected function object_mouseDownHandler (event:MouseEvent):void
		{
			if ( event.isDefaultPrevented() )
			{
				return;
			}

			var object:UIComponent = UIComponent(event.currentTarget);

			currentResizeMethod = findResizeType( object, event.localX, event.localY );

			if ( currentResizeMethod >= 0 )
			{
				var allowedResizes:Array = getAllowedResizesForObject(object);
				if (!allowedResizes || !allowedResizes[currentResizeMethod])
				{
					return;
				}
				
				event.preventDefault();
				event.stopImmediatePropagation();
//				selectComponent( object );
				object.graphics.clear();
				Application.application.parent.addEventListener(MouseEvent.MOUSE_MOVE, 
															application_mouseMoveHandler, false, 0, true);
				Application.application.parent.addEventListener(MouseEvent.MOUSE_UP, 
															application_mouseUpHandler, false, 0, true);
				// WTF? tengo que restarle 4 pixeles para que agarre la coordenada correcta! Serán los estilos???
				startResizePosition = new Point ( event.stageX-4, event.stageY-4 );
				currentWidth = object.width;
				currentHeight = object.height;
				
				rubberBand = new RubberBand ( );

				if ( (object.parent) && !object.parent.contains( rubberBand ) )
				{
					object.parent.addChildAt( rubberBand, object.parent.numChildren );
					setComponentNewSize( rubberBand, object );
					rubberBand.visible = true;
					resizingObject = object;
				}
			}
		}

		/**
		 * Mouse move handler for resize
		 */
		protected function application_mouseMoveHandler(event:MouseEvent):void 
		{
			var xPlus:Number = Application.application.parent.mouseX - startResizePosition.x;			
			var yPlus:Number = Application.application.parent.mouseY - startResizePosition.y;
			
			setNewSize( rubberBand, resizingObject, xPlus, yPlus );
		}

		/**
		 * Handler for the end of the resize
		 */
		protected function application_mouseUpHandler(event:MouseEvent):void 
		{
			// removes the listeners
			Application.application.parent.removeEventListener(MouseEvent.MOUSE_MOVE, application_mouseMoveHandler);
			Application.application.parent.removeEventListener(MouseEvent.MOUSE_UP, application_mouseUpHandler);

			if (resizingObject)
			{
				setComponentNewSize ( resizingObject, rubberBand );
			}
            rubberBand.move(0,0);
			rubberBand.setActualSize(0,0);
			rubberBand.visible = false;
			rubberBand.parent.removeChild(rubberBand);

			currentWidth = 0;
			currentHeight = 0;
			startResizePosition = null; 
			resizingObject = null;
		}	
	}
}